#include "ConversationManager.h"
#include "../Graphic/GraphicManager.h"
#include "../Input/InputManager.h"
#include "../Game/InputConfiguration.h"

//Parse xml to process a node of the conversation tree
void cConversationManager::ParseNode(TiXmlElement *pElem, cConversationNode *pCurrentNode)
{
  //Base case
  if (pElem == NULL)
  {
    pCurrentNode->meType = eEndConversation;
    return;
  }

  //Get the type of node from the XML
  std::string lacTag = pElem->ValueStr();
  if (lacTag == "Talk")
  {
    //Set the type
    pCurrentNode->meType = eNormalTalk;
    
    //Get the text
    const char *lacText = pElem->GetText();
    assert(lacText);
    pCurrentNode->macText = lacText;

    //Get the speaker id
    pElem->QueryIntAttribute("speakerId", &(pCurrentNode->miCharacterId));
    assert(pCurrentNode->miCharacterId >= 0);
    assert(pCurrentNode->miCharacterId < (int)mCharacters.size());

    //Get the duration
    pElem->QueryFloatAttribute("time", &(pCurrentNode->mfDuration));

    //Preparare the next node
    cConversationNode lNode;
    pCurrentNode->mChildren.push_back(lNode);

    //Recursive call
    ParseNode( pElem->NextSiblingElement(), &(pCurrentNode->mChildren[0]));
  }
  else if (lacTag == "ChooseTalk")
  {
    //Set the type
    pCurrentNode->meType = eChooseTalk;

    //Get all the options
    TiXmlElement *pElem2 = pElem->FirstChildElement();
    for (pElem2; pElem2; pElem2 = pElem2->NextSiblingElement())
    {
      assert(pElem2);
      assert(pElem2->ValueStr() == "Option");

      //Add node to the childrens vector
      cConversationNode lNode;
      pCurrentNode->mChildren.push_back(lNode);

      //Recursive calll
      unsigned luiLastIndex = pCurrentNode->mChildren.size() - 1;
      ParseNode( pElem2->FirstChildElement(), &(pCurrentNode->mChildren[luiLastIndex]));
    }
  }
  else
  {
    //Invalid tag found
    assert(0 && "Wrong tag\n");
  }
}

//Parse xml to process a character
void cConversationManager::ParseCharacter(TiXmlElement *pElem)
{
  cCharacterSpeaker lCharacter;

  //Get character Id
  pElem->QueryIntAttribute("speakerId", &lCharacter.miCharacterId );
  assert(lCharacter.miCharacterId >= 0);

  //Get character name
  const char *lacName = pElem->GetText();
  assert(lacName);
  lCharacter.macCharacterName = lacName;

  //Push back into character list
  mCharacters.push_back(lCharacter);
}

//Parse xml to process a conversation
void cConversationManager::ParseConversation(TiXmlElement *pElem)
{
  cConversation lConversation;

  //Get the conversation ID
  const char *lacNameId = pElem->Attribute("nameId");
  assert(lacNameId);
  lConversation.macName = lacNameId;

  //Get the conversation tree
  ParseNode( pElem->FirstChildElement(), &lConversation.mRoot);

  //Push back into conversations list
  mConversations.push_back(lConversation);
}

//Change to the next message
void cConversationManager::NextMessage(unsigned int luiNextMessageIndex)
{
  assert(mpCurrentConversationNode);
  assert(luiNextMessageIndex < mpCurrentConversationNode->mChildren.size());

  //Change to the next node
  bool lbIsChooseNode = (mpCurrentConversationNode->meType == eChooseTalk);
  mpCurrentConversationNode = &(mpCurrentConversationNode->mChildren[luiNextMessageIndex]);

  //Finish the conversation if we reach to the leaf node
  if (mpCurrentConversationNode->meType == eEndConversation)
  {
    mpCurrentConversationNode = NULL;
    return;
  }

  //If the node is a choose node skip the option selected node
  if (lbIsChooseNode)
    NextMessage(0);
  else
  {
    //Establish the actual node values
    muiChooseOption = 0;
    mfMessageTime = mpCurrentConversationNode->mfDuration;
  }
}

//Initializate Conversation Manager
void cConversationManager::Init(const std::string &lacConversationFile)
{
  TiXmlDocument lxDoc(lacConversationFile);

  //Load document
  bool lbLoadOk = lxDoc.LoadFile();

  //If fail goes out
  if (!lbLoadOk )
  {
    std::cout<<"XML Load: FAILED\n";
    system("pause");
    exit(-1);
  }

  //Get the root node
 	TiXmlHandle lhRoot(&lxDoc);
	TiXmlElement *lpElem;
  TiXmlElement *lpRoot;
  
  lpRoot = lxDoc.RootElement();
  
  //Read characters
  lpElem = lpRoot->FirstChild("Characters")->FirstChildElement();
  for(lpElem; lpElem; lpElem = lpElem->NextSiblingElement())
  {
    ParseCharacter(lpElem);
  }

  //Read conversations
  lpElem = lpRoot->FirstChildElement("Conversation");
  for(lpElem; lpElem; lpElem = lpElem->NextSiblingElement())
  {
    ParseConversation(lpElem);
  }

  mpCurrentConversationNode = NULL;
  muiChooseOption = 0;
}

//Deinitializate Conversation Manager
void cConversationManager::Deinit()
{
  //Free characters
  mCharacters.clear();
  
  //Free conversations
  mConversations.clear();
}

//Update the current conversation flow
void cConversationManager::Update(float lfTimeStep)
{
  //If not in conversation goes out
  if (!IsInConversation()) return;
  switch(mpCurrentConversationNode->meType)
  {
    case eNormalTalk:
      //Reduce show time
      mfMessageTime -= lfTimeStep;

      //If time has finished or Accept action has been selected change to the next message
      if (mfMessageTime <= 0.0f )
        NextMessage(0);
      break;
    case eChooseTalk:
      //Only if the user has accepted an option, change to that branch
      if (IsPressed(eIA_Accept))
      {
        NextMessage(muiChooseOption);
        return;
      }
      if (IsPressed(eIA_MoveUp) && muiChooseOption > 0)
      {
        muiChooseOption--;
        return;
      }
      if (IsPressed(eIA_MoveDown) && muiChooseOption < (mpCurrentConversationNode->mChildren.size() - 1))
      {
        muiChooseOption++;
        return;
      }
      break;
    default:
      break;
  }
}

//Render the current conversation
void cConversationManager::Render()
{
  //If not in conversation skip the render method
  if (!IsInConversation()) return;

  //Get the graphic manager
  cGraphicManager &lGraphic = cGraphicManager::Get();
  //Variable that will hold the message
  std::string lsMessage;
  //Variable taht will hold the name of the character talking
  std::string lsCharacterName;
  //Counter for children
  unsigned luiCounter = 0;

  switch (mpCurrentConversationNode->meType)
  {
    case eNormalTalk:
      //Draw the name
      lsCharacterName = mCharacters[(mpCurrentConversationNode->miCharacterId)].macCharacterName + ": ";
      lGraphic.SetColor(eLightWhite, eBlack);
      lGraphic.WriteChars(0, 20, (char *)lsCharacterName.c_str());
      
      //Draw the text
      lGraphic.SetColor(eWhite, eBlack);
      lsMessage = mpCurrentConversationNode->macText;
      lGraphic.WriteChars(lsCharacterName.size(), 20, (char *)lsMessage.c_str());
      break;
    case eChooseTalk:
      //Draw the options
      for (cConversationChildrenIt lcIt = mpCurrentConversationNode->mChildren.begin(); lcIt != mpCurrentConversationNode->mChildren.end(); lcIt++)
      {
        //Set the propper color to the selected option
        if (luiCounter != muiChooseOption)
          lGraphic.SetColor(eWhite, eBlack);
        else
          lGraphic.SetColor(eLightWhite, eBlack);
        
        //Draw the child
        lsMessage = (*lcIt).macText;
        lGraphic.WriteChars(0, 20+luiCounter, (char *)lsMessage.c_str());
        luiCounter++;
      }
      break;
    default:
      break;
  }

}

//Start a conversation
void cConversationManager::StartConversation(const std::string &lacConversationId)
{
  //Seek for conversation
  for (cConversationsIt lcIt = mConversations.begin(); lcIt != mConversations.end(); lcIt++)
  {
    //If the conversation is found assign the root node
    if ((*lcIt).macName == lacConversationId)
    {
      mpCurrentConversationNode = &((*lcIt).mRoot);
      //Also set the message time and the option
      mfMessageTime = (*lcIt).mRoot.mfDuration;
      muiChooseOption = 0;
      break;
    }
  }
}

//Check if the game is in a conversation
bool cConversationManager::IsInConversation()
{
  if (mpCurrentConversationNode)
    return true;
  else
    return false;
}

